/**
 * RegistrationServerImpl.java
 *
 * @author Beatrice Bacelli
 * @author Luca Bedogni
 * @author Silvia Righini
 */
package unibo.lsb.server;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.RemoteServer;
import java.rmi.server.ServerNotActiveException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import unibo.lsb.communication.CommunicationRemote;
import unibo.lsb.communication.ConnectionUtils;
import unibo.lsb.exception.TokioException;
import unibo.lsb.graphic.ServerWindowSingleton;
import unibo.lsb.logic.PlayerId;

public class RegistrationServerImpl extends UnicastRemoteObject implements
		RegistrationServer {

	List<Game> listOfGames;
	CommunicationRemote is;

	public RegistrationServerImpl() throws RemoteException {
		super();
		listOfGames = new ArrayList<Game>();
	}

	public void run() throws TokioException {
		try {
			ConnectionUtils.launchRMIRegistry();

			ServerWindowSingleton.getInstance().getServerWindow().addText(
					"Starting Server...");
			ConnectionUtils.bindService(new RegistrationServerImpl(),
					ConfigurationConstants.SERVER_IP,
					ConfigurationConstants.RMIREGISTRYPORT,
					ConfigurationConstants.REGISTRATIONSERVER_NAME);

			ServerWindowSingleton.getInstance().getServerWindow().addText(
					"Server Running");
			// }
		} catch (RemoteException e) {
			System.err.println(e.getMessage() + "\n");
			e.printStackTrace();
			throw new TokioException("Error connecting", "Connection error");
		} catch (MalformedURLException e) {
			System.err.println(e.getMessage() + "\n");
			e.printStackTrace();
			throw new TokioException("Error connecting", "Connection error");
		}
	}

	public List<Game> getGameList() throws TokioException, RemoteException {
		return listOfGames;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see unibo.lsb.server.RegistrationServer#createGame(java.lang.String,
	 * int)
	 */
	@Override
	public boolean createGame(String gameName, int players)
			throws TokioException, RemoteException {
		boolean found = false;
		Game g = new GameImpl(gameName, players);

		Iterator<Game> i = listOfGames.iterator();
		while (i.hasNext() && !found) {
			Game game = i.next();
			if (game.getName().equals(gameName)) {
				found = true;
			}
		}

		if (!found) {
			ServerWindowSingleton.getInstance();
			ServerWindowSingleton.getServerWindow().addText(
					"Creating game `" + gameName + "`");
			listOfGames.add(g);
		} else {
			ServerWindowSingleton.getInstance().getServerWindow().addText(
					"Game `" + gameName
							+ "` already exists and can not be created.");
			throw new TokioException(
					"A game with this name already exists on server. Could you please choose another one?",
					"Duplicated name");
		}
		return true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see unibo.lsb.server.RegistrationServer#joinGame(java.lang.String,
	 * java.lang.String, java.lang.String)
	 */
	public PlayerId joinGame(String gameName, String playerName,
			String password, int port) throws TokioException, RemoteException {

		PlayerId newPlayer = null;
		Game g = findGame(gameName);

		if (g != null) {
			try {
				String clientHost = RemoteServer.getClientHost();
				String iconNumber = "00";

				do {
					iconNumber = PlayerId.generateRandomIcon();
				} while (iconDuplicate(iconNumber, g));

				newPlayer = new PlayerId(playerName, password, clientHost, port);
				g.addPlayer(newPlayer);
				ServerWindowSingleton.getInstance().getServerWindow().addText(
						"Player " + playerName + " at " + clientHost + ":"
								+ port + " joined the game");

			} catch (ServerNotActiveException e) {
				System.err.println("Error in getting client host");
				throw new TokioException("Error in getting client host.",
						"Error in getting client host");
			}
		}
		return newPlayer;
	}

	/**
	 * Checks if iconNumber is already an used icon in Game g.
	 * 
	 * @param iconNumber
	 * @param g
	 * @return true if the icon is a duplicate
	 */
	private boolean iconDuplicate(String iconNumber, Game g) {
		boolean b = false;
		List<PlayerId> l = g.getListOfPlayers();
		if (!l.isEmpty()) {
			Iterator<PlayerId> ite = l.iterator();
			while (ite.hasNext() && !b) {
				PlayerId p = ite.next();
				if ((p.getIconNumber()).equals(iconNumber)) {
					b = true;
				}
			}
		}
		return b;
	}

	private Game findGame(String gameName) {
		Game g = null;
		boolean found = false;
		if (!listOfGames.isEmpty()) {
			List<Game> l = listOfGames;
			Iterator<Game> i = l.iterator();

			while (i.hasNext() && !found) {
				g = i.next();
				if (g.getName().equals(gameName)) {
					found = true;
				}
			}
		}
		return g;
	}

	@Override
	public boolean confirm(String gameName) throws TokioException,
			RemoteException {

		final Game g = findGame(gameName);

		if (g != null && g.isFull()) {
			// broadcast of PlayerId list when all the players have registered
			// themselves
			ServerWindowSingleton.getInstance().getServerWindow().addText(
					"Game `" + gameName + "` is full");
			List<PlayerId> PlayersList = g.getListOfPlayers();
			Iterator<PlayerId> it = PlayersList.iterator();
			PlayerId p = null;

			while (it.hasNext()) {
				p = it.next();
				try {
					// Connect to RMI at serverAddr:1099
					String objectURL = "rmi://" + p.getIpAddress() + ":"
							+ p.getPort() + "/"
							+ ConfigurationConstants.INFORMATIONSERVER_NAME;
					is = (CommunicationRemote) Naming.lookup(objectURL);

					Thread t = new Thread() {
						@Override
						public void run() {
							try {
								is.receiveGameDetails(g);
							} catch (RemoteException e) {
								e.printStackTrace();
							}
						}
					};
					t.start();

					ServerWindowSingleton.getInstance().getServerWindow()
							.addText(
									"Confirmed game `" + g.getName()
											+ "` to player " + p.getName()
											+ " at " + p.getIpAddress() + ":"
											+ p.getPort());
				} catch (MalformedURLException e) {
					e.printStackTrace();
					throw new TokioException("Bad url", "URL Error");
				} catch (RemoteException e) {
					e.printStackTrace();
					throw new TokioException("Error connecting",
							"ConnectionError");
				} catch (NotBoundException e) {
					e.printStackTrace();
					throw new TokioException("Error connecting",
							"ConnectionError");
				}
				if (is == null) {
					System.err.println("Error: InformationServer is null");
					throw new TokioException("Error connecting",
							"ConnectionError");
				}
			}
			ServerWindowSingleton.getInstance().getServerWindow().addText(
					"Game `" + g.getName() + "` started.");
			deleteGameFromServer(g);
		}
		return false;
	}

	private boolean deleteGameFromServer(Game g) {
		listOfGames.remove(g);
		ServerWindowSingleton.getInstance().getServerWindow().addText(
				"Game `" + g.getName() + "` deleted form server.");
		return true;
	}
}